package com.asen.commons.core.jdbc.result;

import com.asen.commons.core.jdbc.handler.TypeHandler;
import com.asen.commons.core.jdbc.handler.TypeHandlerHolder;
import com.asen.commons.core.meta.entity.EntityObject;
import com.asen.commons.core.meta.entity.FieldObject;
import com.asen.commons.core.meta.loader.EntityObjectLoader;
import com.asen.commons.core.utils.EntityUtils;
import com.asen.commons.utils.BaseAssert;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.JdbcUtils;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;

/**
 * 对象映射
 *
 * @author Gary
 * @version 1.0
 * @date 2018-11-28
 */
public class BaseRowMapper<T> implements RowMapper<T> {

    private Class<T> modelClazz;

    private EntityObject entityObject;

    public BaseRowMapper(Class<T> modelClazz) {
        this.modelClazz = modelClazz;
        this.entityObject = EntityObjectLoader.getInstance().get(modelClazz);
    }

    @Override
    public T mapRow(ResultSet rs, int rowNum) throws SQLException {
        T model = null;

        try {
            model = modelClazz.newInstance();
            ResultSetMetaData rsmd = rs.getMetaData();
            int columnCount = rsmd.getColumnCount();
            for (int i = 1; i <= columnCount; i++) {
                String key = getColumnKey(rsmd, i);
                Object value = rs.getObject(key);
                if (value != null) {
                    setColumnValue(entityObject, model, key, value);
                }
            }
        } catch (Exception e) {
            throw new SQLException(e);
        }

        return model;
    }

    private String getColumnKey(ResultSetMetaData rsmd, int i) throws Exception {
        return JdbcUtils.lookupColumnName(rsmd, i);
    }

    private void setColumnValue(EntityObject eo, Object model, String key, Object value) throws Exception {
        if (value == null) {
            return;
        }

        if (String.class.isAssignableFrom(value.getClass())
                && value.toString().trim().length() == 0) {
            return;
        }

        int index = key.indexOf(".");
        if (index <= 0) {
            FieldObject fo = eo.getFieldObject(key);
            Class<?> javaType = fo.getJavaType();

            if (EntityUtils.isSimpleType(javaType)) {
                TypeHandler handler = TypeHandlerHolder.getHandler(javaType);
                fo.setValue(model, handler.getValue(value));
                return;
            }

            // 数据字典
            //if (fo.isDict()) {
            //    return;
            //}

            // 其它对象
            EntityObject fieldEo = EntityObjectLoader.getInstance().load(javaType);
            BaseAssert.check(fieldEo.hasField("id"), "不支持没有ID的类型【" + javaType.getName() + "】");

            Object fieldModel = fo.getValue(model);
            if (fieldModel == null) {
                fieldModel = javaType.newInstance();
                fo.setValue(model, fieldModel);
            }

            FieldObject subFo = fieldEo.getFieldObject("id");
            subFo.setValue(fieldModel, value);
            return;
        }

        // 复杂属性
        String fieldName = key.substring(0, index);
        String subName = key.substring(index + 1, key.length());
        FieldObject fo = entityObject.getFieldObject(fieldName);
        Class<?> javaType = fo.getJavaType();
        Object fieldModel = fo.getValue(model);
        if (fieldModel == null) {
            fieldModel = javaType.newInstance();
            fo.setValue(model, fieldModel);
        }
        EntityObject fieldEo = EntityObjectLoader.getInstance().load(javaType);
        setColumnValue(fieldEo, fieldModel, subName, value);
    }

    private Class<T> getRefRawType() {
        Type genericSuperclass = this.getClass().getGenericSuperclass();
        Type rawType = ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0];

        if (rawType instanceof ParameterizedType) {
            rawType = ((ParameterizedType) rawType).getRawType();
        }

        return (Class<T>) rawType;
    }

}
